package com.cango.student.reference;

import com.cango.student.reference.cache.EmployeeCache;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 强，软，弱，幻象引用（虚引用）
 */
public class ReferenceTest {
    public static void main(String[] args) {
//        test1();//软引用
//        test2();//弱引用
//        test3();//幽灵引用_1
        test4();//幽灵引用_2
//        test5();//幽灵引用_2
    }

    public static void test1() {
        //在堆中创建一个对象Obj
        //在栈中创建一个p来强引用此对象Obj
        Person p = new Person(1);

        //在栈中创建一个softReference来软引用此对象Obj 可以获取对象的属性值
        SoftReference<Person> softReference = new SoftReference<Person>(p);
        System.out.println(p.getId());//输出打印:1
        System.out.println(softReference.get().getId());//输出打印:1

        //断开p和Obj的强引用
        p = null;
        //System.out.println(p.getId());
        System.gc();
        System.out.println(softReference.get().getId());//输出打印:1
        //并不报空指针异常  虽然断开了p和Obj的强引用,但是并没有被回收.
        //如果在前面调用gc() 垃圾回收  运行结果也是打印1的..软引用只有系统在发生内存溢出异常之前，会把只被软引用的对象进行回收
    }

    public static void test2() {

        //Phantom 幻影幽灵 的意思
        ReferenceQueue<Person> referenceQueue = new ReferenceQueue<Person>();

        //在堆中创建一个对象Obj
        //在栈中创建一个p来强引用此对象Obj
        Person p = new Person(1);

        //在栈中创建一个weakReference来弱引用此对象Obj 可以获取对象的属性值
        WeakReference<Person> weakReference = new WeakReference<Person>(p, referenceQueue);
        System.out.println(weakReference.get().getId());//打印输出:1

        //创建一个线程监听清除的对象
        Thread thread = new Thread(() -> {
            try {
                int cnt = 0;
//                WeakReference<byte[]> k;
                WeakReference<Person> k;
                // 取得一个弱引用
                while ((k = (WeakReference) referenceQueue.remove()) != null) {
                    System.out.println("回收了:" + k);
                    System.out.println("回收了:" + k.get());
                    System.exit(0);
                }
            } catch (InterruptedException e) {
                //结束循环
            }
        });
        thread.start();

//        WeakReference<Person> weakReference2 = new WeakReference<Person>(new Person(2));
//        System.out.println(weakReference2.get().getId());//打印输出:2
        //断开p和Obj的强引用
        p = null;

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.gc();

//        System.out.println(weakReference2.get().getId());//打印输出:1
        //p=null 之后 还是可以正常的打印输出1  说明断开强引用和其他弱引用,软引用压根没有关系.
        //如果在打印之前 调用gc() 方法之后  就会报错..java.lang.NullPointerException
        //垃圾回收不论内存是否不足都会回收只被弱引用关联的对象。

    }

    public static void test3() {
        //在堆中创建一个对象Obj
        //在栈中创建一个p来强引用此对象Obj
        Person p = new Person(1);

        //Phantom 幻影幽灵 的意思
        ReferenceQueue<Person> referenceQueue = new ReferenceQueue<Person>();
        //在栈中创建一个phantomReference来虚引用此对象Obj 不可以获取对象的属性值
        PhantomReference<Person> phantomReference = new PhantomReference<Person>(p, referenceQueue);
        System.out.println(phantomReference.get().getId());//打印报错 java.lang.NullPointerException
        //直接得不到p对象对应的id值....
        //PhantomReference的唯一作用就是 能在这个对象被收集器回收时收到一个系统通知 看test4()方法
    }

    public static void test4() {

        //在堆中创建一个对象Obj
        //在栈中创建一个p来强引用此对象Obj
        Person p = new Person(1);

        //Phantom 幻影幽灵 的意思
        ReferenceQueue<Person> referenceQueue = new ReferenceQueue<Person>();
        //在栈中创建一个phantomReference来虚引用此对象Obj 不可以获取对象的属性值
        PhantomReference<Person> phantomReference = new PhantomReference<Person>(p, referenceQueue);

        System.out.println(referenceQueue.poll());//打印输出: null  这个是查询队列中是否有元素.
        //断开p和obj的强引用
        p = null;
        System.gc();//p被回收之后 队列referenceQueue中就有值了.

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 过一秒钟之后再查询队列中是否有元素.
//        System.out.println(referenceQueue.poll());//打印输出: java.lang.ref.PhantomReference@77fef1a0
        PhantomReference<Person> k;
        // 取得一个弱引用
        while ((k = (PhantomReference) referenceQueue.poll()) != null) {
            System.out.println("回收了:" + k);
            System.out.println("回收了:" + k.get());
            System.exit(0);
        }
        //PhantomReference的唯一作用就是 能在这个对象被收集器回收时收到一个系统通知
        //如果这个对象被回收了,会把通知放到队列中.

        //如果前面p=null注释掉  再运行打印输出就是  null  因为p没有被回收(强引用中) 就不会把通知放到队列中...队列中为空 null
        //回收的标志就是把通知放到队列中..
    }

    public static void test5() {
        Map<Object, Object> map = new HashMap<>();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
        //创建一个线程监听清除的对象
        Thread thread = new Thread(() -> {
            try {
                PhantomPerson k;
                while ((k = (PhantomPerson) referenceQueue.remove()) != null) {
                    try {
                        System.out.println("回收了:" + k.getId());
                    } catch (Exception w) {
                        w.printStackTrace();
                    }

                }
            } catch (InterruptedException e) {
                //结束循环
            }
        });
        thread.setDaemon(true);
        thread.start();

        //Phantom 幻影幽灵 的意思
        for (int i = 0; i < 990000; i++) {
            PhantomPerson phantomPerson = new PhantomPerson(new Person(i), referenceQueue);
            map.put(String.valueOf(i), phantomPerson);
        }


//        System.gc();//p被回收之后 队列referenceQueue中就有值了.

    }

    //继承SoftReference,实现对对象的软引用
    //这个类所引用的目标对象会在JVM内存不足时自动回收
    static class PhantomPerson extends PhantomReference<Person> {

        private Integer id;

        public Integer getId() {
            return id;
        }

        public PhantomPerson(Person referent, ReferenceQueue<Object> queue) {
            super(referent, queue);
            this.id = referent.getId();
        }

    }
}

class Person {
    public Person(Integer id) {
        this.id = id;
    }

    private Integer id;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    // 任意一个对象的finalize只至多执行一次，一旦对象变为Finalized，就怎么也不会在回到F-Queue去了。当然没有机会再执行finalize了。
//    @Override
//    protected void finalize() throws Throwable {
//        System.out.println("finalized!!!!!:" + id);
//    }
}